#pragma once
#include "../Util/util.hpp"
#include <mutex>
#include <thread>
#include <list>
#include <unistd.h>
#include "db.hpp"
#include "online.hpp"
#include "room.hpp"

template<class T>
class match_queue
{
private:
    std::list<T> _list;
    std::mutex _mutex;
    std::condition_variable _cond;
public:
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    void push(const T& data)
    {
        // 加入并唤醒
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }
    bool pop(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty()) return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }
    void remove(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
};

class matcher
{
private:
    match_queue<uint64_t> _q_normal;
    match_queue<uint64_t> _q_high;
    match_queue<uint64_t> _q_super;
    std::thread _th_normal;
    std::thread _th_high;
    std::thread _th_super;
    user_table* _ut;
    room_manage* _rm;
    online_manager* _om;

    void th_handler(match_queue<uint64_t>& q)
    {
        while(true)
        {
            // 队列个数<2 q阻塞
            while(q.size() < 2)
            {
                q.wait();
            }
            // 出队列2个， 失败则重新加入队列
            uint64_t uid1, uid2;
            bool ret = q.pop(uid1);
            if(!ret) continue;
            ret = q.pop(uid2);
            if(!ret)
            {
                this->add(uid1);
                continue;
            }
            // 判断是否掉线，掉线重新加入队列
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if(conn1.get() == nullptr)
            {
                this->add(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if(conn2.get() == nullptr)
            {
                this->add(uid1);
                continue;
            }
            // 创建房间并添加2个玩家, 失败重新加入队列
            room_ptr rp = _rm->create_room(uid1, uid2);
            if(rp.get() == nullptr)
            {
                LOG(ERROR) << "为两个玩家创建房间失败" << "\n";
                this->add(uid1);
                this->add(uid2);
                sleep(1);
                continue; 
            }
            // 发送消息
            std::string json_str;
            Json::Value root;
            root["optype"] = "match_success";
            root["result"] = true;
            Json_Util::serializa(root, json_str);
            conn1->send(json_str);
            conn2->send(json_str);
        }
    }
    void th_normal_func()
    {
        th_handler(_q_normal);
    }
    void th_high_func()
    {
        th_handler(_q_high);
    }
    void th_super_func()
    {
        th_handler(_q_super);
    }
    
public:
    matcher(user_table* ut, room_manage* rm, online_manager* om)
        :_ut(ut),_rm(rm),_om(om),
        _th_normal(std::thread(&matcher::th_normal_func, this)),
        _th_high(std::thread(&matcher::th_high_func, this)),
        _th_super(std::thread(&matcher::th_super_func, this))
    {
        LOG(INFO) << "创建游戏对战匹配完毕" << "\n";
    }
    ~matcher()
    {
        _th_normal.join();// 记得释放线程资源
        _th_high.join();
        _th_super.join();
        LOG(INFO) << "销毁游戏对战匹配成功" << "\n";
    }
    bool add(uint64_t uid)
    {
        Json::Value json_value;
        // 先获取用户信息，再根据分数加入具体哪个队列
        bool ret = _ut->sel_by_id(uid, json_value);
        if(!ret)
        {
            return false;
        }
        uint64_t score = json_value["score"].asInt64();
        if(score < 2000)
        {
            _q_normal.push(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _q_high.push(uid);
        }
        else 
        {
            _q_super.push(uid);
        }
        return true;
    }
    bool del(uint64_t uid)
    {
        Json::Value json_value;
        // 先获取用户信息，再根据分数在具体哪个队列删除用户
        bool ret = _ut->sel_by_id(uid, json_value);
        if(!ret)
        {
            return false;
        }
        uint64_t score = json_value["score"].asInt64();
        if(score < 2000)
        {
            _q_normal.remove(uid);
        }
        else if(score >= 2000 && score < 3000)
        {
            _q_high.remove(uid);
        }
        else 
        {
            _q_super.remove(uid);
        }
        return true;
    }
};